﻿using System;
using System.Collections.Generic;
using NUnit.Framework;

namespace Cpuss.Strategies.Tests
{

    /// <summary>
    /// Tests for Round Robin strategy.
    /// </summary>
    [TestFixture]
    public class RoundRobinTest
    {

        /// <summary>
        /// Check to see that the metrics gathered are as expected when the quantum is
        /// two.
        /// </summary>
        [Test]
        public void QuantumTwoTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 5, 0),
                new Process(2, 7, 3),
                new Process(3, 8, 1)
            };
            Runner actual = new Runner(processLoad, new RoundRobin(2));
            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 5, 0)
            {
                StartTime = 0,
                CompletionTime = 11
            });
            expected.Add(2, new Process(2, 7, 3)
            {
                StartTime = 6,
                CompletionTime = 20
            });
            expected.Add(3, new Process(3, 8, 2)
            {
                StartTime = 2,
                CompletionTime = 19
            });

            actual.Run();

            for (int i = 1; i <= expected.Count; i++)
            {
                Assert.AreEqual(actual.Metrics[i].StartTime, expected[i].StartTime);
                Assert.AreEqual(actual.Metrics[i].CompletionTime, expected[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that the strategy handles any idle cpu times appropriatley.
        /// </summary>
        [Test]
        public void IdleCpuTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 3, 2),
                new Process(2, 1, 3)
            };
            Runner actual = new Runner(processLoad, new RoundRobin(3));
            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 3, 2)
            {
                StartTime = 2,
                CompletionTime = 5
            });
            expected.Add(2, new Process(2, 1, 3)
            {
                StartTime = 5,
                CompletionTime = 6
            });
            
            actual.Run();

            for (int i = 1; i <= expected.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual.Metrics[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual.Metrics[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that when the time quantum used for the round-robin strategy is less than 1 the
        /// correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void TimeQuantumLessThanOneTest()
        {
            Runner actual = new Runner(0, 20, 0, new RoundRobin(0));
        }

        /// <summary>
        /// Check to make sure that the correct metrics are returned when there are a few chunks
        /// of idle cpu time throughout the simulation.
        /// </summary>
        [Test]
        public void SporadicIdleCpuTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 6, 4),
                new Process(2, 3, 3),
                new Process(3, 12, 22),
                new Process(4, 9, 31)
            };
            Runner actual = new Runner(processLoad, new RoundRobin(5));
            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 6, 4)
            {
                StartTime = 6,
                CompletionTime = 12
            });
            expected.Add(2, new Process(2, 3, 3)
            {
                StartTime = 3,
                CompletionTime = 6
            });
            expected.Add(3, new Process(3, 12, 22)
            {
                StartTime = 22,
                CompletionTime = 39
            });
            expected.Add(4, new Process(4, 9, 31)
            {
                StartTime = 32,
                CompletionTime = 43
            });

            actual.Run();

            for (int i = 1; i <= expected.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual.Metrics[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual.Metrics[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that the correct string is returned.
        /// </summary>
        [Test]
        public void ToStringTest()
        {
            RoundRobin actual = new RoundRobin(10);

            Assert.AreEqual("Round Robin (Time Quantum = 10)", actual.ToString());
        }

    }

}
